Skip to content

feat: 버스 정류장 시내버스 예상 도착 시간 데이터 패칭 추가#33

Merged
KwonDeaGeun merged 7 commits intomainfrom
feat/add-busstop-remaintime-data-fetch
Nov 20, 2025
Merged

feat: 버스 정류장 시내버스 예상 도착 시간 데이터 패칭 추가#33
KwonDeaGeun merged 7 commits intomainfrom
feat/add-busstop-remaintime-data-fetch

Conversation

@KwonDeaGeun
Copy link
Owner

@KwonDeaGeun KwonDeaGeun commented Nov 20, 2025

Summary by CodeRabbit

  • 신기능

    • 실시간 버스 도착 정보 추가(정류장별 도착 버스 목록, 남은 분·잔여좌석 표시)
    • 버스별 도착 시간과 노선 색상 구분으로 화면에 렌더링
  • 데이터/지역화

    • 새로운 정류장 "인문관" 추가
    • 도착 없음·곧 도착 등 안내 문구 한국어/영어 번역 추가
  • 잡무

    • 버스 위치·도착 데이터 자동 갱신 주기 7s → 30s로 조정

✏️ Tip: You can customize this high-level summary in your review settings.

@KwonDeaGeun KwonDeaGeun self-assigned this Nov 20, 2025
@vercel
Copy link

vercel bot commented Nov 20, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
what-the-bus-web Ready Ready Preview Comment Nov 20, 2025 5:37pm

@coderabbitai
Copy link

coderabbitai bot commented Nov 20, 2025

Walkthrough

BUS.ARRIVALS 기반 실시간 도착 API와 useBusArrivals 훅을 추가하고, 버스 위치 소스는 SHUTTLE.LOCATIONS로 이동했습니다. Bubble 컴포넌트에 도착 정보 통합, 정류장 인문관 및 관련 번역 키와 데이터가 추가되었습니다. 오류는 onError 콜백으로 전달됩니다.

Changes

코호트 / 파일(들) 변경 요약
API: arrivals & locations
src/api/bus.ts
ArrivalsResponse, Shuttle 타입 사용 추가. useBusArrivals(onError?) 훅 추가(BUS.ARRIVALS 호출, 에러 시 onError 호출하고 null 반환). 위치 호출은 SHUTTLE.LOCATIONS로 전환하고, 비배열 응답에 대해 빈 배열 팔백. 재요청 주기(폴링)는 30초로 설정(도착).
엔드포인트 설정
src/lib/endpoints.ts
API_ENDPOINTS 구조 변경: BUS.ARRIVALS: "api/bus/arrivals"로 설정, 신규 SHUTTLE.LOCATIONS: "api/shuttle/locations" 추가.
UI: Bubble 통합
src/components/Bubble.tsx
useBusArrivals 도입으로 정류장명 매칭(인문관 포함) 기반 도착 목록 렌더링. routeName 기준 색상 매핑, minutesLeft에 따른 라벨(noArrival/arrivingSoon/숫자) 처리 및 arrivals 변경 시 재렌더링.
타입 정의 추가
src/types/bus.ts
Shuttle, ArrivalBus, ArrivalStop, ArrivalsResponse 인터페이스 추가. 여러 모듈의 타입 임포트 전환(예: BusShuttle).
훅/유틸 시그니처 업데이트
src/hooks/*, src/utils/*
useBusSelection, useMapOverlays, createBusOverlays 등에서 입력 타입을 Bus[]Shuttle[]로 변경; 관련 임포트 경로 조정.
데이터·번역·정류장 추가
src/data/busStops.ts, src/contexts/LanguageContext.tsx
새 정류장 인문관 추가(lat: 37.322209, lng: 127.128268) 및 번역 키 busStop.인문관, common.noArrival, common.arrivingSoon(ko/en) 추가.
삭제: 기존 버스 데이터 모델
src/data/bus.ts
Bus 인터페이스와 buses 상수 제거(파일의 기존 데이터 모델 삭제).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Bubble as Bubble 컴포넌트
    participant Hook as useBusArrivals 훅
    participant API as BUS.ARRIVALS API

    User->>Bubble: 마운트/렌더
    Bubble->>Hook: useBusArrivals(onError?)
    Hook->>API: GET /api/bus/arrivals
    API-->>Hook: ArrivalsResponse (stops, updatedAt)
    Hook-->>Bubble: data 또는 null
    Bubble->>Bubble: stopName으로 정류장 필터링(예: 인문관)
    Bubble-->>User: 정류장별 버스 목록 및 시간 라벨

    Note over Hook: 자동 재요청 주기: 30초 (백그라운드 포함)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

  • 주의 필요 영역:
    • src/components/Bubble.tsx: 정류장 부분 문자열 매칭 로직과 arrivals null/엣지 케이스 처리
    • src/api/bus.ts: 훅들의 반환값(빈 배열 vs null) 일관성 및 onError 메시지 매핑
    • src/lib/endpoints.ts: 엔드포인트 키 변경으로 인한 다른 호출부 영향 범위

Possibly related PRs

Poem

🐰 달빛 아래 정류장에 귀 기울여요,
인문관 새 번호판에 발자국 대신 소식이 와요.
30초마다 속닥이는 도착 알림,
NULL도 웃음으로 바꿔주는 당근 한 조각,
토끼가 전하는 작은 길안내 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 주요 변경 사항을 명확하게 반영하고 있습니다. 버스 정류장의 시내버스 예상 도착 시간 데이터 패칭 기능 추가라는 핵심 변경사항이 한국어로 간결하게 표현되어 있습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/add-busstop-remaintime-data-fetch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/components/Bubble.tsx (1)

24-47: arrivals 의존성으로 7초마다 오버레이 전체 재마운트 가능성 (선택적 리팩터)

useBusArrivals가 7초마다 refetch하면서 arrivals 객체가 갱신될 때마다 이 useEffect가 다시 실행되고, 내부에서 clearExisting() → 새 overlay 생성 흐름이라, 도착 정보가 갱신될 때마다 오버레이가 언마운트/리마운트되는 플리커가 발생할 여지가 있습니다. 체감상 문제가 된다면:

  • 오버레이 생성/제거는 stop, onClose만 의존하는 이펙트로 두고,
  • 내부 내용은 별도 React 컴포넌트로 빼서 arrivals를 props로 넘겨 일반적인 리렌더링만 일어나게 하거나,
  • 전역(예: window.__currentBubbleOverlay)에 root를 캐싱해 두고 동일 overlay 위에서만 root.render를 다시 호출

하는 방향으로 분리해 보면 좋겠습니다. 급한 이슈는 아니라 optional refactor로 봐도 될 것 같습니다.

Also applies to: 270-291

src/api/bus.ts (1)

30-67: 도착 정보 타입/훅 설계는 명확하나, 필요 시 쿼리 활성화 제어 옵션 고려 권장

ArrivalBus / ArrivalStop / ArrivalsResponse로 도착 정보를 명시적으로 모델링하고, useBusArrivals에서 ArrivalsResponse | null을 반환하는 구조는 타입 측면에서 깔끔합니다. 다만 현재 구현대로면 Bubble 컴포넌트에서 이 훅을 사용하는 동안(오버레이가 닫혀 있어도) 7초마다 항상 도착 정보 API가 호출될 수 있습니다.

향후 트래픽이나 성능 이슈가 보이면:

  • useBusArrivals(enabled: boolean, onError?: ...)처럼 enabled 플래그를 인자로 받게 하고,
  • 내부 useQueryenabled 옵션을 전달하거나,
  • 혹은 쿼리 키에 정류장 정보 등을 포함해 필요한 경우에만 구독하도록

조정할 수 있도록 설계 여지를 열어 두는 것도 좋을 것 같습니다. 지금 단계에서는 선택적 리팩터로 봐도 될 것 같습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a092c75 and 7434015.

📒 Files selected for processing (5)
  • src/api/bus.ts (2 hunks)
  • src/components/Bubble.tsx (5 hunks)
  • src/contexts/LanguageContext.tsx (4 hunks)
  • src/data/busStops.ts (1 hunks)
  • src/lib/endpoints.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: KwonDeaGeun
Repo: KwonDeaGeun/WhatTheBus-Web PR: 21
File: src/App.tsx:16-23
Timestamp: 2025-09-22T04:45:54.455Z
Learning: KwonDeaGeun은 TypeScript에서 any 타입 사용을 피하고 더 타입 안전한 코드를 선호한다.
🧬 Code graph analysis (2)
src/components/Bubble.tsx (1)
src/api/bus.ts (1)
  • useBusArrivals (48-67)
src/api/bus.ts (4)
src/lib/api.ts (1)
  • apiGet (14-22)
src/data/bus.ts (1)
  • Bus (1-6)
src/lib/endpoints.ts (1)
  • API_ENDPOINTS (1-9)
src/lib/error.ts (1)
  • handleApiError (9-49)
🔇 Additional comments (4)
src/contexts/LanguageContext.tsx (1)

20-21: 신규 버스 정류장/도착 상태 번역 키 일관성 양호

busStop.인문관, common.noArrival, common.arrivingSoon 모두 데이터(busStops.ts)와 UI(Bubble.tsx) 사용처와 키가 잘 맞고, Record<string, string>으로 any 없이 타입도 유지돼서 i18n 측면에서 무리 없어 보입니다. Based on learnings

Also applies to: 44-46, 56-57, 80-81

src/data/busStops.ts (1)

11-12: 신규 정류장 '인문관' 추가 일관성 OK

정류장 이름 "인문관"이 번역 키(busStop.인문관)와 Bubble 컴포넌트의 DISPLAY_NAME_MAP 키와도 일치해서, 클릭 시 도착 정보 매칭에도 문제 없을 것 같습니다. 좌표만 실제 지도 기준으로 한 번 눈으로만 검증해 두면 좋겠습니다.

src/lib/endpoints.ts (1)

2-8: BUS.LOCATION → BUS.ARRIVALS, SHUTTLE.LOCATIONS 구조 변경 확인 필요

셔틀 위치(SHUTTLE.LOCATIONS)와 일반 버스 도착(BUS.ARRIVALS)을 분리한 구조는 명확해서 좋습니다. 다만 예전에 쓰이던 API_ENDPOINTS.BUS.LOCATION 참조가 남아 있으면 바로 런타임 에러가 날 수 있으니, 레포 전체에서 해당 키가 더 이상 사용되지 않는지 한 번만 검색해서 확인해 주세요.

src/api/bus.ts (1)

12-14: 셔틀 위치 조회 엔드포인트 전환 시 응답 스키마 확인 필요

useBusLocations가 이제 API_ENDPOINTS.SHUTTLE.LOCATIONS에서 Bus[]를 기대하고 있는데, 새 셔틀 API가 정말로 순수 배열을 바로 반환하는지(예: { data: Bus[] } 같은 래퍼 없이) 백엔드 스펙을 한 번만 확인해 주세요. 만약 래퍼 객체로 바뀌어 있다면 현재 구현은 Array.isArray(data) ? data : [] 때문에 조용히 빈 배열로 떨어져 실제 위치가 표시되지 않을 수 있습니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/api/bus.ts (2)

24-27: 위치(7초)와 도착(30초) refetch 주기 차이가 의도된 설계인지 점검 제안

useBusLocations는 7초, useBusArrivals는 30초로 refetch 주기가 다르게 잡혀 있습니다. 실시간성이 더 중요한 쪽에 맞춘 의도된 값이라면 그대로 두어도 되지만, 둘을 동일 주기로 맞추는 것이 목표였다면 상수로 추출해서 공유하거나 주기를 명시적으로 설명하는 주석을 남겨 두면 유지보수 관점에서 더 명확해질 것 같습니다.


48-67: useBusArrivals의 에러 처리 및 null 처리에 대한 소소한 개선 제안

현재 try/catch에서 에러를 모두 처리하고 null을 반환하기 때문에 React Query 입장에서는 항상 “성공 + data가 null일 수 있음” 상태가 되고, isError/error는 사용되지 않게 됩니다. 이 패턴을 의도하신 것이라면 괜찮지만, 훅을 소비하는 쪽에서 React Query의 에러 상태를 활용하고 싶다면 onError 콜백 호출 후에 에러를 그대로 throw하는 쪽도 고려해 볼 수 있습니다. 또한,

return data || null;

보다는

-                const data = await apiGet<ArrivalsResponse>(
-                    API_ENDPOINTS.BUS.ARRIVALS
-                );
-                return data || null;
+                const data = await apiGet<ArrivalsResponse>(
+                    API_ENDPOINTS.BUS.ARRIVALS
+                );
+                return data ?? null;

처럼 null 병합 연산자를 사용하면, 혹시나 나중에 ArrivalsResponse가 빈 문자열 같은 다른 falsy 값으로 확장되더라도 의도치 않게 null로 처리되는 일을 막을 수 있어 조금 더 안전합니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7434015 and bc66338.

📒 Files selected for processing (1)
  • src/api/bus.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: KwonDeaGeun
Repo: KwonDeaGeun/WhatTheBus-Web PR: 21
File: src/App.tsx:16-23
Timestamp: 2025-09-22T04:45:54.455Z
Learning: KwonDeaGeun은 TypeScript에서 any 타입 사용을 피하고 더 타입 안전한 코드를 선호한다.
🧬 Code graph analysis (1)
src/api/bus.ts (3)
src/lib/api.ts (1)
  • apiGet (14-22)
src/lib/endpoints.ts (1)
  • API_ENDPOINTS (1-9)
src/lib/error.ts (1)
  • handleApiError (9-49)
🔇 Additional comments (2)
src/api/bus.ts (2)

12-21: SHUTTLE.LOCATIONS 응답 스키마가 Bus[]와 정확히 일치하는지 한 번 더 확인해 주세요

BUS.LOCATION에서 API_ENDPOINTS.SHUTTLE.LOCATIONS로 엔드포인트를 바꾸고 그대로 apiGet<Bus[]>()를 사용하고 있습니다. 런타임에서는 Array.isArray로 방어하고 있지만, 실제 응답이 Bus[] 인터페이스와 완전히 일치하지 않으면 타입/도메인 불일치가 생길 수 있으니, 백엔드 스펙과 맞는지 한 번만 점검해 보시고 필요하다면 여기에서 매핑/변환 레이어를 두는 편이 더 안전합니다.


30-46: 도착 정보 타입 정의는 명확하지만 null/undefined 케이스만 한 번 더 확인 필요

minutesLeft, remainingSeatsnumber | null로 정의해 둔 점은 도메인 의미가 잘 드러나서 좋습니다. 다만 백엔드에서 필드를 아예 생략하는 경우가 있다면 런타임 값은 undefined가 될 수 있으니, 응답 스펙이 “항상 필드는 존재하지만 값이 없을 때는 null”인지 한 번 더 확인해 보시고, 필요하다면 파싱 단계에서 기본값을 보정해 두는 것도 고려해 볼 만합니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/utils/mapOverlays.ts (1)

3-3: Shuttle 기반으로 오버레이를 키잉하도록 변경된 점이 타입과 일관적입니다.

Shuttle 타입을 도입해 createBusOverlays(map, buses: Shuttle[]) 시그니처와 캐시 키(bus.shuttleId)가 맞춰진 부분 좋습니다. Shuttle.shuttleId가 항상 유효한 값으로 들어온다는 전제가 있다면, ${bus.lat}-${bus.lng} fallback 이 실제로 필요한지(또는 assert 로 바꿀지) 한 번 더 정리해 두면 의도가 더 명확해질 것 같습니다.

Also applies to: 285-310

src/hooks/useBusSelection.ts (1)

2-2: Shuttle[] 기반 선택 로직으로의 타입 정리가 자연스럽습니다.

buses: Shuttle[]로 바뀌어 위치/방향 필드 사용이 명시적이고, 상위 useBusLocations 결과와도 타입이 잘 맞습니다. idx = n - 1는 UI에서 항상 1부터 시작하는 입력을 전제로 하므로, 방어적으로 1 ≤ n ≤ buses.length 범위 체크를 추가해 두면 추후 호출부 변경에도 더 안전할 것 같습니다.

Also applies to: 6-6, 11-36

src/types/bus.ts (1)

1-23: 셔틀·도착 정보 타입 정의가 직관적이고 null 가능성이 잘 표현되어 있습니다.

API 응답 형태를 Shuttle, ArrivalBus, ArrivalStop, ArrivalsResponse로 구분해 둔 덕분에 각 계층 책임이 명확하고, minutesLeft/remainingSeatsnull 여부도 타입에서 잘 드러납니다. 나중에 클라이언트 내부 도메인 모델을 더 풍부하게 가져가고 싶다면(예: updatedAtDate로 파싱한 뷰 모델 등) 이 파일의 원본 응답 타입과 별도의 도메인 타입을 분리해 두는 것도 고려해 볼 만합니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc66338 and f9c1408.

📒 Files selected for processing (6)
  • src/api/bus.ts (2 hunks)
  • src/data/bus.ts (0 hunks)
  • src/hooks/useBusSelection.ts (1 hunks)
  • src/hooks/useMapOverlays.ts (2 hunks)
  • src/types/bus.ts (1 hunks)
  • src/utils/mapOverlays.ts (2 hunks)
💤 Files with no reviewable changes (1)
  • src/data/bus.ts
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: KwonDeaGeun
Repo: KwonDeaGeun/WhatTheBus-Web PR: 21
File: src/App.tsx:16-23
Timestamp: 2025-09-22T04:45:54.455Z
Learning: KwonDeaGeun은 TypeScript에서 any 타입 사용을 피하고 더 타입 안전한 코드를 선호한다.
🧬 Code graph analysis (3)
src/hooks/useBusSelection.ts (1)
src/types/bus.ts (1)
  • Shuttle (1-6)
src/utils/mapOverlays.ts (1)
src/types/bus.ts (1)
  • Shuttle (1-6)
src/hooks/useMapOverlays.ts (1)
src/types/bus.ts (1)
  • Shuttle (1-6)
🔇 Additional comments (2)
src/hooks/useMapOverlays.ts (1)

3-3: Shuttle 타입으로의 전환이 잘 정리되어 있습니다.

버스 데이터 입력을 Shuttle[]로 고정해 createBusOverlays 시그니처와 일치시키면서 타입 안정성이 올라갔고, 기존 이펙트 로직과 의도도 그대로 유지되어 보입니다.

Also applies to: 14-14

src/api/bus.ts (1)

5-5: Shuttle 위치/도착 데이터 훅 설계가 타입·엔드포인트와 잘 정합됩니다.

useBusLocations가 항상 Shuttle[]를(비정상 응답/에러 시에도 []) 반환하고, useBusArrivalsArrivalsResponse | null을 반환하도록 한 패턴이 명확해서 호출부에서 분기 처리하기 좋아 보입니다. 다만 소비 측(예: Bubble 컴포넌트)에서 data === null(호출 실패)과 data.stops.length === 0(정상 호출 + 데이터 없음)을 어떻게 구분할지 한 번 더 확인해 두면 UX 측면에서 의도된 상태 표현을 보장할 수 있을 것 같습니다.

Also applies to: 12-22, 32-50

@KwonDeaGeun KwonDeaGeun merged commit 34713cc into main Nov 20, 2025
4 checks passed
@KwonDeaGeun KwonDeaGeun deleted the feat/add-busstop-remaintime-data-fetch branch November 20, 2025 17:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant